#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXLINE 10
#define OPEN_MAX 100
#define LISTENQ 20
#define SERV_PORT 54321
#define INFTIM 1000

void setnonblocking(int sock)
{
    int opts;
    opts = fcntl(sock, F_GETFL);
    if(opts < 0)
    {
        perror("fcntl(sock,GETFL)");
        exit(1);
    }
    opts = opts | O_NONBLOCK;
    if(fcntl(sock, F_SETFL, opts) < 0)
    {
        perror("fcntl(sock,SETFL,opts)");
        exit(1);
    }
}

int main()
{
    int i, maxi, listenfd, connfd, sockfd, epfd, nfds;
    int n;
    char line[MAXLINE];
    socklen_t clilen;
    //声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件
    struct epoll_event ev, events[20];
    //生成用于处理accept的epoll专用的文件描述符
    epfd = epoll_create(256);

    struct sockaddr_in clientaddr;
    struct sockaddr_in serveraddr;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    //把socket设置为非阻塞方式
    setnonblocking(listenfd);
    //设置与要处理的事件相关的文件描述符
    ev.data.fd = listenfd;
    //设置要处理的事件类型
    ev.events = EPOLLIN | EPOLLET;
    //注册epoll事件
    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;

    char *local_addr = "127.0.0.1";
    inet_aton(local_addr, &(serveraddr.sin_addr));
    serveraddr.sin_port = htons(SERV_PORT);
    if (bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) {
        printf("can not bind to port %d\n", SERV_PORT);
        return 0;
    }
    
    printf("server is listening\n");
    listen(listenfd, LISTENQ);

    maxi = 0;
    for ( ; ; )
    {
        nfds = epoll_wait(epfd, events, 20, 500);
        for(i = 0; i < nfds; ++i)
        {
            if(events[i].data.fd == listenfd)
            {
                if (!(events[i].events & EPOLLIN)) {
                    continue;
                }

                connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clilen);
                printf("connfd is %d\n", connfd);
                if(connfd < 0)
                {
                    continue;
                }
                setnonblocking(connfd);

                char *str = inet_ntoa(clientaddr.sin_addr);
                ev.data.fd = connfd;
                ev.events = EPOLLIN | EPOLLET;
                epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
            }
            else if(events[i].events & EPOLLIN)
            {
                if ( (sockfd = events[i].data.fd) < 0) continue;
                if ( (n = read(sockfd, line, MAXLINE)) < 0)
                {
                    if (errno == ECONNRESET)
                    {
                        close(sockfd);
                        printf("client %d exited...\n", sockfd);
                        events[i].data.fd = -1;
                    }
                    else
                        printf("readline error\n");
                }
                else if (n == 0)
                {
                    close(sockfd);
                    printf("client %d exited.\n", sockfd);
                    events[i].data.fd = -1;
                }
                else {
                    line[n] = '\0';
                    printf("recv %s from %d\n", line, sockfd);
                    ev.data.fd = sockfd;
                    ev.events = EPOLLOUT | EPOLLET;
                    epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
                }
            }
            else if(events[i].events & EPOLLOUT)
            {
                sockfd = events[i].data.fd;
                printf("send %s:%d to client %d\n", line, n, sockfd);
                write(sockfd, line, n);
                ev.data.fd = sockfd;
                ev.events = EPOLLIN | EPOLLET;
                epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
            }
        }
    }
}
